/**
 ******************************************************************************
 * @file    can.c
 * @author  hyseim software Team
 * @date    12-Mar-2024
 * @brief   This file provides all the tmp functions.
 ******************************************************************************
 * @attention
 *
 * Copyright (c) 2020 Hyseim. Co., Ltd.
 * All rights reserved.
 *
 * This software is licensed under terms that can be found in the LICENSE file
 * in the root directory of this software component.
 * If no LICENSE file comes with this software, it is provided AS-IS.
 *
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "can.h"
#include "common.h"

/** @addtogroup IM110GW_Driver
 * @{
 */

/** @defgroup CAN
 * @brief CAN driver modules
 * @{
 */

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/**
 * @brief  Initializes the CANx peripheral according to the specified
 *         parameters in the CAN_InitStruct.
 * @param  CANx: Pointer to selected CAN peripheral.
 *         This parameter can be one of the following values:
 *         CAN0, CAN1, CAN2.
 * @param  CAN_InitStruct: pointer to a CAN_Init_t structure
 *         that contains the configuration information for the specified
 *         CAN peripheral.
 * @return None
 */
void CAN_Init(CAN_t *CANx, CAN_Init_t *CAN_InitStruct)
{
    CAN_UnlockReg(CANx);
    CANx->S_SEG_1 = CAN_InitStruct->SlowBaudRate.CAN_S_seg1;
    CANx->S_SEG_2 = CAN_InitStruct->SlowBaudRate.CAN_S_seg2;
    CANx->S_SJW   = CAN_InitStruct->SlowBaudRate.CAN_S_SJW;
    CANx->S_PRESC = CAN_InitStruct->SlowBaudRate.CAN_S_Prescaler;

    if (CAN_FDF_FD_Frame == CAN_InitStruct->CAN_Specification)
    {
        CAN_FD_AddInit(CANx, &CAN_InitStruct->CAN_FD_AddInitStruct);
    }
    CAN_LockReg(CANx);

    if (DISABLE == CAN_InitStruct->CAN_Transmit_STB_FIFO_MODE)
    {
        CANx->TCTRL |= CAN_TSMODE;
    }
    else
    {
        CANx->TCTRL &= ~CAN_TSMODE;
    }

    if (DISABLE == CAN_InitStruct->CAN_Retransmission)
    {
        CANx->CFG_STAT |= (CAN_TPSS | CAN_TSSS);
    }
    else
    {
        CANx->CFG_STAT &= ~(CAN_TPSS | CAN_TSSS);
    }

    if (ENABLE == CAN_InitStruct->CAN_ReceiveLocked)
    {
        CANx->RCTRL |= CAN_ROM;
    }
    else
    {
        CANx->RCTRL &= ~CAN_ROM;
    }

    if (DISABLE == CAN_InitStruct->CAN_TimeTriggerEnable)
    {
        CANx->TTCFG &= ~CAN_TTEN;
    }
    else
    {
        CANx->TTCFG |= CAN_TTEN;
    }

    switch (CAN_InitStruct->CAN_OperatingMode)
    {
        case CAN_LOOPBACK_INTERNAL:
            CANx->CFG_STAT |= CAN_LBMI;
            break;
        case CAN_LOOPBACK_EXTERNAL:
            CANx->CFG_STAT |= CAN_LBME;
            CANx->RCTRL |= CAN_SACK;
            break;
        case CAN_LISTENONLY:
            CANx->TCMD |= CAN_LISTENONLY;
            break;
        case CAN_LISTENONLY_LBME:
            CANx->CFG_STAT |= CAN_LBME;
            CANx->TCMD |= CAN_LISTENONLY;
            break;
        case CAN_NORMAL_COMMUNICATE:
            /*default opertating mode (RESET)*/
            break;
        default:
            break;
    }
}

/**
 * @brief  Initializes the CANx peripheral according to the specified
 *         parameters in the CAN_FD_AddInit_t for CAN FD.
 * @param  CANx: Pointer to selected CAN peripheral.
 *         This parameter can be one of the following values:
 *         CAN0, CAN1, CAN2.
 * @param  CAN_FD_AddInitStruct: pointer to a CAN_FD_AddInit_t structure
 *         that contains the configuration information for the specified
 *         CAN peripheral.
 * @return None
 */
void CAN_FD_AddInit(CAN_t *CANx, CAN_FD_AddInit_t *CAN_FD_AddInitStruct)
{
    /*set CAN FD ISO mode*/
    if (ENABLE == CAN_FD_AddInitStruct->CAN_FD_ISO_Mode)
    {
        CANx->TCTRL |= CAN_FD_ISO;
    }
    else
    {
        CANx->TCTRL &= ~CAN_FD_ISO;
    }
    CAN_PRINT("BRS:0x%x\n",CAN_FD_AddInitStruct->CAN_FD_FastBaudRate_Enable);
    if (ENABLE == CAN_FD_AddInitStruct->CAN_FD_FastBaudRate_Enable)
    {
        if (ENABLE == CAN_FD_AddInitStruct->TransmitterDelayCompensation.CAN_TDCEN)
        {
            CANx->TDC = CAN_TDC_Enable | CAN_FD_AddInitStruct->TransmitterDelayCompensation.CAN_SSPOFF;
        }
        else
        {
            CANx->TDC &= ~CAN_TDC_Enable;
        }
        CAN_PRINT("F_seg1:0x%x\n",CAN_FD_AddInitStruct->FastBaudRate.CAN_F_seg1);
        CAN_PRINT("F_seg2:0x%x\n",CAN_FD_AddInitStruct->FastBaudRate.CAN_F_seg2);
        CAN_PRINT("F_SJW :0x%x\n",CAN_FD_AddInitStruct->FastBaudRate.CAN_F_SJW);
        CAN_PRINT("F_Pres:0x%x\n",CAN_FD_AddInitStruct->FastBaudRate.CAN_F_Prescaler);
        CANx->F_SEG_1 = CAN_FD_AddInitStruct->FastBaudRate.CAN_F_seg1;
        CANx->F_SEG_2 = CAN_FD_AddInitStruct->FastBaudRate.CAN_F_seg2;
        CANx->F_SJW = CAN_FD_AddInitStruct->FastBaudRate.CAN_F_SJW;
        CANx->F_PRESC = CAN_FD_AddInitStruct->FastBaudRate.CAN_F_Prescaler;
    }
}
#if 0
/**
 * @brief  set CAN pin.
 * @param  CANx: Pointer to selected CAN peripheral.
 *         This parameter can be one of the following values:
 *         CAN0, CAN1, CAN2.
 * @param  CAN_InitStruct: pointer to a CAN_Init_t structure
 *         that contains the configuration information for the specified
 *         CAN peripheral.
 * @return None
 */
void CAN_IOmuxCfg(CAN_t *CANx, CAN_Init_t *CAN_InitStruct)
{
    CAN_t *base = CANx;
    switch ((uint32_t)base)
    {
    case (uint32_t)CAN0:
        xio_set(CAN0_TX, CAN_InitStruct->PINx_Init.CAN_TXD_Pin0, 0);
        xio_set(CAN0_RX, CAN_InitStruct->PINx_Init.CAN_RXD_Pin1, 0);
        xio_set(CAN0_STBY, CAN_InitStruct->PINx_Init.CAN_STB_Pin2, 0);
        break;
    case (uint32_t)CAN1:
        xio_set(CAN1_TX, CAN_InitStruct->PINx_Init.CAN_TXD_Pin0, 0);
        xio_set(CAN1_RX, CAN_InitStruct->PINx_Init.CAN_RXD_Pin1, 0);
        xio_set(CAN1_STBY, CAN_InitStruct->PINx_Init.CAN_STB_Pin2, 0);
        break;
    case (uint32_t)CAN2:
        xio_set(CAN2_TX, CAN_InitStruct->PINx_Init.CAN_TXD_Pin0, 0);
        xio_set(CAN2_RX, CAN_InitStruct->PINx_Init.CAN_RXD_Pin1, 0);
        xio_set(CAN2_STBY, CAN_InitStruct->PINx_Init.CAN_STB_Pin2, 0);
        break;
    default:
        break;
    }
}
#endif
/**
 * @brief  Fills each CAN_InitStruct member with its default value.
 * @param  CAN_InitStruct: pointer to a CAN_Init_t structure
 *         which will be initialized.
 * @return None
 */
void CAN_StructInit(CAN_Init_t *CAN_InitStruct)
{
    CAN_InitStruct->CAN_Specification = CAN_FDF_2_Frame;
    CAN_InitStruct->CAN_Retransmission = DISABLE;
    CAN_InitStruct->CAN_Transmit_STB_FIFO_MODE = DISABLE;
    CAN_InitStruct->CAN_ReceiveLocked = ENABLE;
    CAN_InitStruct->CAN_TimeTriggerEnable = DISABLE;
    CAN_InitStruct->CAN_OperatingMode = CAN_NORMAL_COMMUNICATE;

    // CAN slow Baudrate = 500kbps (can_clk=40M)
    CAN_InitStruct->SlowBaudRate.CAN_S_seg1 = S_seg1_500K;
    CAN_InitStruct->SlowBaudRate.CAN_S_seg2 = S_seg2_500K;
    CAN_InitStruct->SlowBaudRate.CAN_S_SJW = S_SJW_500K;
    CAN_InitStruct->SlowBaudRate.CAN_S_Prescaler = S_Prescaler_500K;
}

/**
 * @brief  Fills each CAN_FD_AddInitStruct member with its default value.
 * @param  CAN_InitStruct: pointer to a CAN_FD_AddInit_t structure
 *         which will be initialized.
 * @return None
 */
void CAN_FD_AddStructInit(CAN_FD_AddInit_t *CAN_FD_AddInitStruct)
{
    /* CAN_FD_AddInitStruct members default value */
    CAN_FD_AddInitStruct->CAN_FD_ISO_Mode = ENABLE;
    CAN_FD_AddInitStruct->CAN_FD_FastBaudRate_Enable = ENABLE;

    // CAN fast Baudrate = 2Mbps (can_clk=40M)
    CAN_FD_AddInitStruct->FastBaudRate.CAN_F_seg1 = F_seg1_2M;
    CAN_FD_AddInitStruct->FastBaudRate.CAN_F_seg2 = F_seg2_2M;
    CAN_FD_AddInitStruct->FastBaudRate.CAN_F_SJW = F_SJW_2M;
    CAN_FD_AddInitStruct->FastBaudRate.CAN_F_Prescaler = F_Prescaler_2M;

    CAN_FD_AddInitStruct->TransmitterDelayCompensation.CAN_TDCEN = ENABLE;
    CAN_FD_AddInitStruct->TransmitterDelayCompensation.CAN_SSPOFF = TDC_SSPOFF_2M;
}

/**
 * @brief  Calculate the length of the transmitted data.
 * @param  fdf: CAN or CANFD reference CAN_Specification.
 * @param  dlc: reference CAN_Definition_of_the_DLC.
 * @return the length of the transmitted data.
 */
uint8_t CAN_DLC_Lenth(uint8_t fdf, uint8_t dlc)
{
    uint8_t frame_len = 0;

    if(CAN_FDF_2_Frame == fdf)//CAN frame 2.0B 
    {
         switch(dlc)
        {
            case 0x0: 
                frame_len = 0;
                break;
            case 0x1: 
                frame_len = 1;
                break;
            case 0x2: 
                frame_len = 2;
                break;
            case 0x3: 
                frame_len = 3;
                break;
            case 0x4: 
                frame_len = 4;
                break;
            case 0x5: 
                frame_len = 5;
                break;
            case 0x6: 
                frame_len = 6;
                break;
            case 0x7: 
                frame_len = 7;
                break;
            case 0x8: 
            case 0x9:
            case 0xa: 
            case 0xb: 
            case 0xc: 
            case 0xd: 
            case 0xe: 
            case 0xf: 
                frame_len = 8;
                break;
            default:
                break;
        }
    }
    else//CAN_FD frame
    {
        switch(dlc)
        {
            case 0x0: 
                frame_len = 0;
                break;
            case 0x1: 
                frame_len = 1;
                break;
            case 0x2: 
                frame_len = 2;
                break;
            case 0x3: 
                frame_len = 3;
                break;
            case 0x4: 
                frame_len = 4;
                break;
            case 0x5: 
                frame_len = 5;
                break;
            case 0x6: 
                frame_len = 6;
                break;
            case 0x7: 
                frame_len = 7;
                break;
            case 0x8: 
                frame_len = 8;
                break;
            case 0x9:
                frame_len = 12;
                break; 
            case 0xa: 
                frame_len = 16;
                break;
            case 0xb: 
                frame_len = 20;
                break;
            case 0xc: 
                frame_len = 24;
                break;
            case 0xd: 
                frame_len = 32;
                break;
            case 0xe: 
                frame_len = 48;
                break;
            case 0xf: 
                frame_len = 64;
                break; 
            default:
                break;
        }
    }
    return  frame_len;
}

/**
 * @brief  Initiates and transmits a CAN frame message using  Primary Transmit Buffers(PTB).
 * @param  CANx: Pointer to selected CAN peripheral.
 *         This parameter can be one of the following values:
 *         CAN0, CAN1, CAN2.
 * @param  txMessage: pointer to a CAN_TxMsg structure
 *         that contains the member required by the message frame.
 * @return None
 */
void CAN_TransmitViaPTB(CAN_t *CANx, CAN_TxMsg *txMessage)
{

    CANx->TCMD &= ~CAN_TBSEL;

    if (txMessage->IDE == CAN_IDE_Id_Extended)
    {
        CANx->TBUF.Identifier = txMessage->ExtId & 0x1FFFFFFF;
        CAN_PRINT("0x%x, 0x%x\n",CANx->TBUF.Identifier, txMessage->ExtId);
    }
    else
    {
        CANx->TBUF.Identifier = txMessage->StdId & 0x7FF;

    }

    CANx->TBUF.Control = txMessage->IDE | txMessage->RTR | txMessage->FDF | \
                         txMessage->BRS | txMessage->DLC;
    CAN_PRINT("0x%x\n",CANx->TBUF.Control);

    uint32_t *pDst = (uint32_t *)CANx->TBUF.DataByte;
    uint32_t *pSrc = (uint32_t *)txMessage->Data;
    uint8_t DLC_num = CAN_DLC_Lenth(txMessage->FDF, txMessage->DLC);
    CAN_PRINT("DLC_num:%d\n",DLC_num);
    uint8_t i;
    for (i = 0; i < (DLC_num >> 2); i++)
    {
        pDst[i] = pSrc[i];
        CAN_PRINT("pDst[%d]:0x%x\n",i, pDst[i]);
    }
    //Transmit Primary Enable
    CANx->TCMD |= CAN_TPE;
}

/**
 * @brief  Initiates and transmits a CAN frame message using  Secondary Transmit Buffer (STB).
 * @param  CANx: Pointer to selected CAN peripheral.
 *         This parameter can be one of the following values:
 *         CAN0, CAN1, CAN2.
 * @param  CAN_TxMsg: pointer to a CAN_TxMsg structure
 *         that contains the member required by the message frame.
 * @param  txMessagNum: The number of CAN message frames to be transmitted.
 * @return None
 */
void CAN_TransmitViaSTB(CAN_t *CANx, CAN_TxMsg *txMessage, uint8_t txMessagNum)
{
    if (txMessagNum > CAN_STB_MAX_NUM)
    {
        return ;
    }
    
    CANx->TCMD |= CAN_TBSEL;
    uint8_t i;
    for (i = 0; i < txMessagNum; i++)
    {
        if (txMessage[i].IDE == CAN_IDE_Id_Extended)
        {
            CANx->TBUF.Identifier = txMessage[i].ExtId & 0x1FFFFFFF;
        }
        else
        {
            CANx->TBUF.Identifier = txMessage[i].StdId & 0x7FF;
        }

        CANx->TBUF.Control = (uint32_t)(txMessage[i].IDE | txMessage[i].RTR | txMessage[i].FDF | \
                             txMessage[i].BRS | txMessage[i].DLC);
        uint32_t *pDst = (uint32_t *)CANx->TBUF.DataByte;
        uint32_t *pSrc = (uint32_t *)txMessage[i].Data;
        uint8_t DLC_num = CAN_DLC_Lenth(txMessage[i].FDF, txMessage[i].DLC);
        uint8_t n;
        CAN_PRINT("DLC_num:0x%x\n", DLC_num);
        for (n = 0; n < (DLC_num >> 2); n++)
        {
            pDst[n] = pSrc[n];
            CAN_PRINT("pDst[%d]:0x%x\n",n, pDst[n]);
        }
        CANx->TCTRL |= CAN_TSNEXT;
    }
}

/**
 * @brief  Transmission enable of one in the STB.
 * @param  CANx: Pointer to selected CAN peripheral.
 *         This parameter can be one of the following values:
 *         CAN0, CAN1, CAN2.
 * @return None
 */
void CAN_TransmitOneFrameSTB(CAN_t *CANx)
{
    CANx->TCMD |= CAN_TSONE;
}

/**
 * @brief  Transmission enable of all messages in the STB.
 * @param  CANx: Pointer to selected CAN peripheral.
 *         This parameter can be one of the following values:
 *         CAN0, CAN1, CAN2.
 * @return None
 */
void CAN_TransmitAllFrameSTB(CAN_t *CANx)
{
    CANx->TCMD |= CAN_TSALL;
}

/**
 * @brief  Transmission enable of all messages in the STB.
 * @param  CANx: Pointer to selected CAN peripheral.
 *         This parameter can be one of the following values:
 *         CAN0, CAN1, CAN2.
 * @param  rxMessage: pointer to a CAN_RxMsg structure
 *         that contains the member required by the message frame.
 * @return None
 */
void CAN_ReceiveMsg(CAN_t *CANx, CAN_RxMsg* rxMessage)
{
    uint8_t i;

    rxMessage->IDE = CANx->RBUF.Control & CAN_IDE_Id_Extended;
    rxMessage->RTR = CANx->RBUF.Control & CAN_RTR_Remote;
    rxMessage->FDF = CANx->RBUF.Control & CAN_FDF_FD_Frame;
    rxMessage->BRS = CANx->RBUF.Control & CAN_BRS_Enable;
    rxMessage->DLC = CANx->RBUF.Control & 0xF;
    
    if(rxMessage->IDE == CAN_IDE_Id_Extended)
    {
        rxMessage->ExtId = CANx->RBUF.Identifier & 0x1FFFFFFF; 
    }
    else
    {
        rxMessage->StdId = CANx->RBUF.Identifier & 0x7FF; 
    }

    if(!rxMessage->RTR)
    {
        uint8_t DLC_num = CAN_DLC_Lenth(rxMessage->FDF, rxMessage->DLC);
        CAN_PRINT("DLC_num:%d\n", DLC_num);
        for (i = 0; i < DLC_num; i ++)
        {
            rxMessage->Data[i] = CANx->RBUF.DataByte[i];
        }
    }
    //Receive buffer RELease
    CANx->RCTRL |= CAN_RREL;
}

/**
 * @brief  Some registers Write unlock.
 * @param  CANx: Pointer to selected CAN peripheral.
 *         This parameter can be one of the following values:
 *         CAN0, CAN1, CAN2.
 * @return None
 */
void CAN_UnlockReg(CAN_t *CANx)
{
    CANx->CFG_STAT |= CAN_RESET;
}

/**
 * @brief  Some registers Write lock.
 * @param  CANx: Pointer to selected CAN peripheral.
 *         This parameter can be one of the following values:
 *         CAN0, CAN1, CAN2.
 * @return None
 */
void CAN_LockReg(CAN_t *CANx)
{
    CANx->CFG_STAT &= ~CAN_RESET;
    while (CANx->CFG_STAT & CAN_RESET)
    {
        /* wait RESET finish */
    }
}

/**
 * @brief  Initializes one of the channels of the filter.
 * @param  CANx: Pointer to selected CAN peripheral.
 *         This parameter can be one of the following values:
 *         CAN0, CAN1, CAN2.
 * @param  CAN_FilterInitStruct: pointer to a CAN_Filter_t structure
 *         that contains the member required by the filter.
 * @return None
 */
void CAN_FilterChannelInit(CAN_t *CANx, CAN_Filter_t *CAN_FilterInitStruct)
{
    CAN_UnlockReg(CANx);
    // disable
    CANx->ACF_EN_0_1 &= ~(0x1 << CAN_FilterInitStruct->CAN_FilterChannel);
    // config acode
    CANx->ACFCTRL &= ~CAN_SEL_MASK;
    CANx->ACFCTRL |= CAN_FilterInitStruct->CAN_FilterChannel;
    CANx->ACF_0_3 = CAN_FilterInitStruct->CAN_Acode;
    // config amask
    CANx->ACFCTRL |= CAN_SEL_MASK;
    CANx->ACFCTRL |= CAN_FilterInitStruct->CAN_FilterChannel;
    CANx->ACF_0_3 = CAN_FilterInitStruct->CAN_Amask;
    CANx->ACF_0_3 |= CAN_FilterInitStruct->CAN_FilterIdMode;
    // enalbe
    CANx->ACF_EN_0_1 |= (0x1 << CAN_FilterInitStruct->CAN_FilterChannel);
    CAN_LockReg(CANx);
}

/**
 * @brief  Disable default filter channel 0().
 *         Because filter channel 0 receives all messages by default
 * @param  CANx: Pointer to selected CAN peripheral.
 *         This parameter can be one of the following values:
 *         CAN0, CAN1, CAN2.
 * @return None
 */
void CAN_FilterDisalbeDefaultChannel_0(CAN_t *CANx)
{
    CAN_UnlockReg(CANx);
    CANx->ACF_EN_0_1 &= ~(0x1 << CAN_FILTER_CH0);
    CAN_LockReg(CANx);
}

/**
 * @brief  Enables or disables the CANx's Receive and Transmit interrupts.
 * @param  CANx: Pointer to selected CAN peripheral.
 *         This parameter can be one of the following values:
 *         CAN0, CAN1, CAN2.
 * @param  CAN_RTIE_IT: specifies the CAN interrupt sources to be enabled or disabled.
 *         This parameter can be a combination of the following values:
 *         @arg @ref CAN_RIE
 *         @arg @ref CAN_ROIE
 *         @arg @ref CAN_RFIE
 *         @arg @ref CAN_RAFIE
 *         @arg @ref CAN_TPIE
 *         @arg @ref CAN_TSIE
 *         @arg @ref CAN_EIE
 * @param  NewState: new state of the interrupts.
 *         This parameter can be: ENABLE or DISABLE.
 * @return None
 */
void CAN_RTIE_ITConfig(CAN_t *CANx, uint8_t CAN_IT, FunctionalState_t NewState)
{
    if (NewState != DISABLE)
    {
        CANx->RTIE |= CAN_IT;
    }
    else
    {
        CANx->RTIE &= ~CAN_IT;
    }
}

/**
 * @brief  Enables or disables the CANx's ERR interrupts.
 * @param  CANx: Pointer to selected CAN peripheral.
 *         This parameter can be one of the following values:
 *         CAN0, CAN1, CAN2.
 * @param  CAN_ERRINT_IT: specifies the CAN interrupt sources to be enabled or disabled.
 *         This parameter can be a combination of the following values:
 *         @arg @ref CAN_EPIE
 *         @arg @ref CAN_ALIE
 *         @arg @ref CAN_BEIE
 * @param  NewState: new state of the interrupts.
 *         This parameter can be: ENABLE or DISABLE.
 * @return None
 */
void CAN_ERR_ITConfig(CAN_t *CANx, uint8_t CAN_IT, FunctionalState_t NewState)
{
    if (NewState != DISABLE)
    {
        CANx->ERRINT |= CAN_IT;
    }
    else
    {
        CANx->ERRINT &= ~CAN_IT;
    }
}

/**
 * @brief  Checks whether the specified CAN Receive and Transmit flag is set or not.
 * @param  CANx: Pointer to selected CAN peripheral.
 *         This parameter can be one of the following values:
 *         CAN0, CAN1, CAN2.
 * @param  CAN_FLAG: specifies the flag to check.
 *         This parameter can be one of the following values:
 *         @arg @ref CAN_RIF: Receive Interrupt Flag.
 *         @arg @ref CAN_ROIF: RB Overrun Interrupt Flag.
 *         @arg @ref CAN_RFIF: RB Full Interrupt Flag.
 *         @arg @ref CAN_RAFIF: RB Almost Full Interrupt Flag.
 *         @arg @ref CAN_TPIF: Transmission Primary Interrupt Flag.
 *         @arg @ref CAN_TSIF: Transmission Secondary Interrupt Flag.
 *         @arg @ref CAN_EIF: Error Interrupt Flag.
 *         @arg @ref CAN_AIF: Abort Interrupt Flag.
 * @return The new state of CAN_FLAG (SET or RESET).
 */
FlagStatus_t CAN_RTIF_GetFlagStatus(CAN_t *CANx, uint32_t CAN_FLAG)
{
    FlagStatus_t bitstatus = RESET;

    if ((CANx->RTIF & CAN_FLAG) != (uint32_t)RESET)
    {
        bitstatus = SET;
    }
    else
    {
        bitstatus = RESET;
    }
    return bitstatus;
}

/**
 * @brief  Checks whether the specified CAN ERROR flag is set or not.
 * @param  CANx: Pointer to selected CAN peripheral.
 *         This parameter can be one of the following values:
 *         CAN0, CAN1, CAN2.
 * @param  CAN_FLAG: specifies the flag to check.
 *         This parameter can be one of the following values:
 *         @arg @ref CAN_EWARN: Error WARNing limit reached.
 *         @arg @ref CAN_EPASS: Error Passive mode active.
 *         @arg @ref CAN_EPIF: Error Passive Interrupt Flag.
 *         @arg @ref CAN_ALIF: Arbitration Lost Interrupt Flag.
 *         @arg @ref CAN_BEIF: Bus Error Interrupt Flag.
 * @return The new state of CAN_FLAG (SET or RESET).
 */
FlagStatus_t CAN_ERR_GetFlagStatus(CAN_t *CANx, uint32_t CAN_FLAG)
{
    FlagStatus_t bitstatus = RESET;

    if ((CANx->ERRINT & CAN_FLAG) != (uint32_t)RESET)
    {
        bitstatus = SET;
    }
    else
    {
        bitstatus = RESET;
    }
    return bitstatus;
}

/**
 * @brief  Clear the specified CAN Receive and Transmit flag is set or not.
 * @param  CANx: Pointer to selected CAN peripheral.
 *         This parameter can be one of the following values:
 *         CAN0, CAN1, CAN2.
 * @param  CAN_FLAG: specifies the flag to check.
 *         This parameter can be one of the following values:
 *         @arg @ref CAN_RIF: Receive Interrupt Flag.
 *         @arg @ref CAN_ROIF: RB Overrun Interrupt Flag.
 *         @arg @ref CAN_RFIF: RB Full Interrupt Flag.
 *         @arg @ref CAN_RAFIF: RB Almost Full Interrupt Flag.
 *         @arg @ref CAN_TPIF: Transmission Primary Interrupt Flag.
 *         @arg @ref CAN_TSIF: Transmission Secondary Interrupt Flag.
 *         @arg @ref CAN_EIF: Error Interrupt Flag.
 *         @arg @ref CAN_AIF: Abort Interrupt Flag.
 * @return None.
 */
void CAN_RTIF_ClearFlag(CAN_t *CANx, uint32_t CAN_FLAG)
{
    CANx->RTIF |= CAN_FLAG;
}

/**
 * @brief  Clear whether the specified CAN ERROR flag is set or not.
 * @param  CANx: Pointer to selected CAN peripheral.
 *         This parameter can be one of the following values:
 *         CAN0, CAN1, CAN2.
 * @param  CAN_FLAG: specifies the flag to check.
 *         This parameter can be one of the following values:
 *         @arg @ref CAN_EWARN: Error WARNing limit reached.
 *         @arg @ref CAN_EPASS: Error Passive mode active.
 *         @arg @ref CAN_EPIF: Error Passive Interrupt Flag.
 *         @arg @ref CAN_ALIF: Arbitration Lost Interrupt Flag.
 *         @arg @ref CAN_BEIF: Bus Error Interrupt Flag.
 * @return None.
 */
void CAN_ERRIF_ClearFlag(CAN_t *CANx, uint32_t CAN_FLAG)
{
    CANx->ERRINT |= CAN_FLAG;
}

/**
 * @brief  Reset the CAN controller register.
 * @param  CANx: Pointer to selected CAN peripheral.
 *         This parameter can be one of the following values:
 *         CAN0, CAN1, CAN2.
 * @return None.
 */
void CAN_Reset(void *CAN_base)
{
    switch ((uint32_t)CAN_base)
    {
        case (uint32_t)CAN0:
            im_write32((CRG_BASE + 0x118), (1 << 6 | (1 << 6) << 16));
            im_write32((CRG_BASE + 0x118), (0 << 6 | (1 << 6) << 16));
            break;
        case (uint32_t)CAN1:
            im_write32((CRG_BASE + 0x118), (1 << 7 | (1 << 7) << 16));
            im_write32((CRG_BASE + 0x118), (0 << 7 | (1 << 7) << 16));
            break;
        case (uint32_t)CAN2:
            im_write32((CRG_BASE + 0x118), (1 << 8 | (1 << 8) << 16));
            im_write32((CRG_BASE + 0x118), (0 << 8 | (1 << 8) << 16));
            break;
        
        default:
            break;
    }
}
